home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume8 / roffix < prev    next >
Encoding:
Text File  |  1989-08-25  |  17.0 KB  |  567 lines

  1. Newsgroups: comp.sources.misc
  2. subject: v08i019: roffix - improve overstriking efficiency in nroff output
  3. From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  4. Reply-To: wilber@homxc.UUCP
  5.  
  6. Posting-number: Volume 8, Issue 19
  7. Submitted-by: wilber@homxc.UUCP
  8. Archive-name: roffix
  9.  
  10. I wrote this little hack when I got tired of watching my dot matrix
  11. printer nearly shake itself to pieces every time it printed out an
  12. nroff file with lots of underlined and boldfaced words.
  13.  
  14. Bob Wilber
  15.  
  16. #! /bin/sh
  17. # This is a shell archive.  Remove anything before this line, then unpack
  18. # it by saving it into a file and typing "sh file".  To overwrite existing
  19. # files, type "sh file -c".  You can also feed this as standard input via
  20. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  21. # will see the following message at the end:
  22. #        "End of archive 1 (of 1)."
  23. # Contents:  README MANIFEST roffix.c roffix.1
  24. # Wrapped by wilber@homxc on Fri Aug 25 16:51:38 1989
  25. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  26. if test -f 'README' -a "${1}" != "-c" ; then 
  27.   echo shar: Will not clobber existing file \"'README'\"
  28. else
  29. echo shar: Extracting \"'README'\" \(1180 characters\)
  30. sed "s/^X//" >'README' <<'END_OF_FILE'
  31. X                 Roffix
  32. X
  33. XThis little program reduces nervous tension for people who use nroff with
  34. Xsingle-character-at-a-time printers such as dot matrix or daisy wheel printers.
  35. XIt rearranges the way backspacing is done for overstriking so as to reduce the
  36. Xnumber of times the print head has to reverse direction.  Now when there are a
  37. Xlot of underlined and boldface words your printer will quickly go zip - zip -
  38. Xzip instead of slowly go rataratarataratarataratarataratarata.
  39. X
  40. XThere's only one source file so a Makefile would be silly.  Define LITTLE_STACK
  41. Xif you have a machine with a small stack limit (less than about 30K bytes).
  42. XDefine BSD if you're on a BSD system (defaults to System V).  You will need
  43. Xgetopt, which is in the public domain.  The "standard" AT&T version of getopt
  44. Xcan be found in Volume 3 of comp.sources.unix, and various gussied up versions
  45. Xare also available.
  46. X
  47. XApparently there's an nroff clone running under MS-DOS that's already smart
  48. Xabout backspacing, so for it this program will have little or no effect.
  49. X
  50. XRoffix is in the public domain and is not a trademark of anybody.
  51. X
  52. XReport bugs to wilber@homxc.att.com, with a *small* nroff file that exhibits
  53. Xthe bug.
  54. END_OF_FILE
  55. if test 1180 -ne `wc -c <'README'`; then
  56.     echo shar: \"'README'\" unpacked with wrong size!
  57. fi
  58. # end of 'README'
  59. fi
  60. if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  61.   echo shar: Will not clobber existing file \"'MANIFEST'\"
  62. else
  63. echo shar: Extracting \"'MANIFEST'\" \(34 characters\)
  64. sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
  65. XREADME
  66. XMANIFEST
  67. Xroffix.c
  68. Xroffix.1
  69. END_OF_FILE
  70. if test 34 -ne `wc -c <'MANIFEST'`; then
  71.     echo shar: \"'MANIFEST'\" unpacked with wrong size!
  72. fi
  73. # end of 'MANIFEST'
  74. fi
  75. if test -f 'roffix.c' -a "${1}" != "-c" ; then 
  76.   echo shar: Will not clobber existing file \"'roffix.c'\"
  77. else
  78. echo shar: Extracting \"'roffix.c'\" \(9357 characters\)
  79. sed "s/^X//" >'roffix.c' <<'END_OF_FILE'
  80. X/*
  81. Xroffix - improve overstriking efficiency in output of nroff
  82. X
  83. XUsage:  nroff -Tlp file | roffix | lp
  84. XSee man page for details.
  85. X
  86. XThis program is in the public domain.
  87. X
  88. XSend bugs to: wilber@homxc.att.com
  89. X(Make sure you send a *small* nroff file that exhibits the problem.)
  90. X*/
  91. X
  92. X#include <errno.h>
  93. X#include <stdio.h>
  94. X#include <ctype.h>
  95. X
  96. X/* Can you say "gratuitous incompatibility?" */
  97. X#ifdef BSD
  98. X#include <strings.h>
  99. X#else
  100. X#include <string.h>
  101. X#endif
  102. X
  103. Xextern int getopt();
  104. Xextern char *optarg;
  105. Xextern int optind;
  106. X
  107. Xextern int errno;
  108. Xextern char *sys_errlist[];
  109. Xextern int sys_nerr;
  110. Xstatic char errmsg[30];
  111. X
  112. X/* #define LITTLE_STACK */
  113. X    /* Define if your machine has a small stack ( <~ 30K ) */
  114. X
  115. X#define MAX_LINE 1000
  116. X    /* Maximum number of characters on a line. */
  117. X
  118. X#define MAX_OVERSTRIKE 20
  119. X    /* Maximum number of characters overprinted in a single position */
  120. X
  121. X#define SINGLE_LIMIT 4
  122. X    /* Default for single_limit, the number of non-overstruck characters
  123. X       that can be between two sequences of overstruck characters and
  124. X       still have the backspacing for the two sequences be done at once.
  125. X       E.g., with single_limit = 2,
  126. X       _^Hf_^Ho_^Ho, _^Hb_^Ha_^Hr & _^Hq_^Hu_^Hu_^Hx
  127. X       becomes
  128. X       foo, bar^H^H^H^H^H^H^H^H___  ___ & quux^H^H^H^H____
  129. X       but with single_limit >= 3 it becomes
  130. X       foo, bar & quux^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H___  ___   ____
  131. X    */
  132. X       
  133. Xtypedef struct
  134. X  {
  135. X    int bx;
  136. X    int max_bx;
  137. X    char buffer[MAX_LINE][MAX_OVERSTRIKE];
  138. X    char next_pos[MAX_LINE];
  139. X  } LineBuffer;
  140. X
  141. X#define ALT_START '\016'
  142. X        /* Start alternate character set. */
  143. X#define ALT_END '\017'
  144. X        /* End alternate character set. */
  145. X#define ESC '\033'
  146. X
  147. Xextern void do_it();
  148. Xextern void dump_buffer();
  149. X
  150. Xstatic int error_cnt = 0;
  151. X
  152. Xchar* errormsg()
  153. X{
  154. X  if (errno <= sys_nerr) return sys_errlist[errno];
  155. X  sprintf(errmsg, "error code %d", errno);
  156. X  return errmsg;
  157. X}
  158. X
  159. Xmain(argc, argv)
  160. Xint argc;
  161. Xchar *argv[];
  162. X{
  163. X  char *in_name;
  164. X  FILE *outfile, *infile;
  165. X  int opt;
  166. X  int single_limit = SINGLE_LIMIT;
  167. X
  168. X  outfile = stdout;
  169. X  while ((opt = getopt(argc, argv, "?o:s:")) != EOF)
  170. X    {
  171. X      switch (opt)
  172. X    {
  173. X    case 'o':
  174. X      if (!(outfile = fopen(optarg, "w")))
  175. X        {
  176. X          fprintf(stderr, "roffix: Open of %s failed.\n%s\n", optarg,
  177. X              errormsg());
  178. X          exit(1);
  179. X        }
  180. X      break;
  181. X    case 's':
  182. X      single_limit = atoi(optarg);
  183. X      break;
  184. X    case '?':
  185. X    default:
  186. X      fputs("usage: roffix [ -o outfile ] [ -s count ] infile ...\n",
  187. X        stderr);
  188. X      exit(1);
  189. X    }
  190. X    }
  191. X  if (optind < argc)
  192. X    {
  193. X      for (; optind < argc; optind++)
  194. X    {
  195. X      in_name = argv[optind];
  196. X      if (access(in_name, 04) || !(infile = fopen(in_name, "r")))
  197. X        {
  198. X          fprintf(stderr, "roffix: Open of %s failed.\n%s\n", in_name,
  199. X              errormsg());
  200. X          error_cnt++;
  201. X        }
  202. X      else do_it(in_name, infile, outfile, single_limit);
  203. X    }
  204. X    }
  205. X  else do_it("stdin", stdin, outfile, single_limit);
  206. X  exit(error_cnt);
  207. X}
  208. X
  209. Xchar* lc2str(i)
  210. Xint i;
  211. X{
  212. X  static char buf[30];
  213. X  int j = i / 2;
  214. X  sprintf(buf, "line %d", j);
  215. X  if (i > 2*j) strcat(buf, " 1/2");
  216. X  return buf;
  217. X}
  218. X
  219. Xvoid do_it(in_fname, infile, outfile, single_limit)
  220. Xchar *in_fname;
  221. XFILE *infile, *outfile;
  222. Xint single_limit;
  223. X{
  224. X  register int inch;
  225. X#ifdef LITTLE_STACK
  226. X  static
  227. X#endif
  228. X  LineBuffer lb;
  229. X  int line_count;   /* Counts half-lines, not full lines. */
  230. X  int left_error;   /* Used to ensure that we print the error about      */
  231. X  int right_error;  /* backspacing before the first column or forward    */
  232. X            /* spacing after the last column only once per line. */
  233. X  int overstrike_error; /* Like above but for exceeding MAX_OVERSTRIKE. */
  234. X  int crossing_error;   /* Like above but for backspace crossing a tab. */
  235. X  char break_char;    /* The character that caused the last dump of the
  236. X               buffer. */
  237. X
  238. X  for (lb.bx = 0; lb.bx < MAX_LINE; lb.bx++) lb.next_pos[lb.bx] = 0;
  239. X  lb.bx = lb.max_bx = 0;
  240. X  line_count = 2;
  241. X  crossing_error = left_error = right_error = overstrike_error = 0;
  242. X  break_char = '\n';
  243. X
  244. X/* lb.bx keeps track of nroff's notion of where the print head is relative
  245. X   to the start of the current buffer.  When lb.bx is < 0 we output backspaces
  246. X   and normal characters immediately, when lb.bx >= 0 printable characters
  247. X   are stuffed into "buffer" for processing by dump_buffer.
  248. X*/
  249. X
  250. X  while ((inch = getc(infile)) != EOF)
  251. X    {
  252. X      switch (inch)
  253. X    {
  254. X    case '\f':
  255. X    case '\n':
  256. X    case '\r':
  257. X      dump_buffer(outfile, &lb, single_limit, 0);
  258. X      break_char = '\n'; /* All are "new lines" for our purposes. */
  259. X      putc(inch, outfile);
  260. X      if (inch != '\r') line_count += 2;
  261. X      crossing_error = left_error = right_error = overstrike_error = 0;
  262. X      break;
  263. X    case '\t':
  264. X      if (lb.bx < lb.max_bx && !crossing_error)
  265. X        {
  266. X          fprintf(stderr,
  267. X      "roffix: warning: %s, %s: Crossing a tab with a backspace.\n",
  268. X              in_fname, lc2str(line_count));
  269. X          crossing_error = 1;
  270. X          error_cnt++;
  271. X        }
  272. X      dump_buffer(outfile, &lb, single_limit, 1);
  273. X      break_char = inch;
  274. X      putc(inch, outfile);
  275. X      break;
  276. X    case ALT_START:
  277. X          /* Alternate character set enabled.  Just dump out the
  278. X         characters "as is" until the normal character set is
  279. X         re-enabled. */
  280. X      dump_buffer(outfile, &lb, single_limit, 1);
  281. X      break_char = inch;
  282. X      putc(inch, outfile);
  283. X      while ((inch = getc(infile)) != ALT_END)
  284. X        {
  285. X          if (inch == EOF)
  286. X        {
  287. X          fprintf(stderr,
  288. X      "roffix: warning: %s: File ended in alternate character set mode.\n",
  289. X                  in_fname);
  290. X          putc(ALT_END, outfile);
  291. X          return;
  292. X        }
  293. X          putc(inch, outfile);
  294. X        }
  295. X      putc(inch, outfile);
  296. X      break;
  297. X    case ESC:
  298. X      dump_buffer(outfile, &lb, single_limit, 1);
  299. X      break_char = inch;
  300. X      putc(inch, outfile);
  301. X      if ((inch = getc(infile)) == EOF) return;
  302. X      putc(inch, outfile);
  303. X      switch (inch)
  304. X        {
  305. X        case '9': line_count += 3;  /* Forward 1/2 line feed. */
  306. X        case '7': line_count--;     /* Reverse line feed. */
  307. X        case '8': line_count--;     /* Reverse 1/2 line feed. */
  308. X          crossing_error = left_error = right_error = overstrike_error = 0;
  309. X          break;
  310. X        default:
  311. X          break;
  312. X        }
  313. X      break;
  314. X    case '\v': /* Alternative to ESC 7 */
  315. X      dump_buffer(outfile, &lb, single_limit, 1);
  316. X      break_char = inch;
  317. X      putc(inch, outfile);
  318. X      line_count -= 2;
  319. X      crossing_error = left_error = right_error = overstrike_error = 0;
  320. X      break;
  321. X    case '\b':
  322. X      lb.bx--;
  323. X      if (lb.bx < 0)
  324. X        {
  325. X          putc(inch, outfile);
  326. X          if (break_char == '\n' && !left_error)
  327. X        {
  328. X          fprintf(stderr,
  329. X    "roffix: warning: %s, %s: Backspacing to left of first column.\n",
  330. X              in_fname, lc2str(line_count));
  331. X          left_error = 1;
  332. X          error_cnt++;
  333. X        }
  334. X          else if (break_char == '\t' && !crossing_error)
  335. X        {
  336. X          fprintf(stderr,
  337. X      "roffix: warning: %s, %s: Crossing a tab with a backspace.\n",
  338. X              in_fname, lc2str(line_count));
  339. X          crossing_error = 1;
  340. X          error_cnt++;
  341. X        }
  342. X        }
  343. X      break;
  344. X    default:
  345. X      if (isprint(inch))
  346. X        {
  347. X          if (lb.bx >= 0)
  348. X        {
  349. X          if (lb.bx >= MAX_LINE)
  350. X            {
  351. X              if (!right_error)
  352. X            {
  353. X              fprintf(stderr,
  354. X       "roffix: warning: %s, %s: Exceeded line limit of %d characters.\n",
  355. X                  in_fname, lc2str(line_count), MAX_LINE);
  356. X              right_error = 1;
  357. X              error_cnt++;
  358. X            }
  359. X            }
  360. X          else
  361. X            {
  362. X              if (inch != ' ')
  363. X            {
  364. X              if (lb.next_pos[lb.bx] >= MAX_OVERSTRIKE)
  365. X                {
  366. X                  if (!overstrike_error)
  367. X                {
  368. X                  fprintf(stderr,
  369. X        "roffix: warning: %s, %s: Exceeded overstrike limit of %d.\n",
  370. X                      in_fname, lc2str(line_count),
  371. X                      MAX_OVERSTRIKE);
  372. X                  overstrike_error = 1;
  373. X                  error_cnt++;
  374. X                }
  375. X                }
  376. X              else lb.buffer[lb.bx][lb.next_pos[lb.bx]++] = inch;
  377. X            }
  378. X              if (++lb.bx > lb.max_bx) lb.max_bx = lb.bx;
  379. X            }
  380. X        }
  381. X          else /* The print head is to the left of the leftmost position
  382. X              in the current buffer, so send out character immediately.
  383. X           */
  384. X        {
  385. X          putc(inch, outfile);
  386. X          ++lb.bx;
  387. X        }
  388. X        }
  389. X      else /* A funny character.  Ship it out and hope it works. */
  390. X        {
  391. X          dump_buffer(outfile, &lb, single_limit, 1);
  392. X          break_char = inch;
  393. X          putc(inch, outfile);
  394. X        }
  395. X    }
  396. X    }
  397. X  if (lb.max_bx > 0)
  398. X      dump_buffer(outfile, &lb, single_limit, 0);
  399. X  return;
  400. X}
  401. X
  402. Xvoid dump_buffer(outfile, lbP, single_lmt, fix_final_pos)
  403. XFILE* outfile;
  404. XLineBuffer *lbP;
  405. Xint single_lmt;
  406. Xint fix_final_pos; /* If true, make sure actual printhead finishes where
  407. X              nroff thinks it should. */
  408. X{
  409. X  register int px = 0;
  410. X  register int qx, rx, sx;
  411. X  int snglecnt;
  412. X    /* Bring the print head up to where it's supposed to be for
  413. X       dumping the buffer. */
  414. X  for (sx = lbP->bx; sx < 0; sx++) putc(' ', outfile);
  415. X  while (1)
  416. X    {
  417. X      while (px < lbP->max_bx && lbP->next_pos[px] <= 1)
  418. X    {
  419. X      if (lbP->next_pos[px] == 0) putc(' ', outfile);
  420. X      else putc(lbP->buffer[px][--lbP->next_pos[px]], outfile);
  421. X      px++;
  422. X    }
  423. X      if (px >= lbP->max_bx) break;
  424. X      qx = px + 1;
  425. X      while (1)
  426. X    {
  427. X      while (qx < lbP->max_bx && lbP->next_pos[qx] > 1) qx++;
  428. X      if (qx >= lbP->max_bx) break;
  429. X      snglecnt = 1;
  430. X      for (rx = qx + 1;
  431. X           rx < lbP->max_bx && lbP->next_pos[rx] <= 1 &&
  432. X                    snglecnt <= single_lmt;
  433. X           rx++) snglecnt++;
  434. X      if (rx >= lbP->max_bx || snglecnt > single_lmt) break;
  435. X      qx = rx + 1;
  436. X    }
  437. X      for (sx = px; sx < qx; sx++)
  438. X    {
  439. X      if (lbP->next_pos[sx] == 0) putc(' ', outfile);
  440. X      else putc(lbP->buffer[sx][--lbP->next_pos[sx]], outfile);
  441. X    }
  442. X      for (sx = px; sx < qx; sx++) putc('\b', outfile);
  443. X    }
  444. X  if (fix_final_pos)
  445. X      for (sx = lbP->bx; sx < lbP->max_bx; sx++) putc('\b', outfile);
  446. X  lbP->bx = lbP->max_bx = 0;
  447. X}
  448. END_OF_FILE
  449. if test 9357 -ne `wc -c <'roffix.c'`; then
  450.     echo shar: \"'roffix.c'\" unpacked with wrong size!
  451. fi
  452. # end of 'roffix.c'
  453. fi
  454. if test -f 'roffix.1' -a "${1}" != "-c" ; then 
  455.   echo shar: Will not clobber existing file \"'roffix.1'\"
  456. else
  457. echo shar: Extracting \"'roffix.1'\" \(3560 characters\)
  458. sed "s/^X//" >'roffix.1' <<'END_OF_FILE'
  459. X.TH ROFFIX 1
  460. X.SH NAME
  461. Xroffix \(mi improve overstriking efficiency in output of nroff
  462. X.SH SYNOPSIS
  463. X\fBroffix\fP [ \(mi\fBs\fP count ] [ \(mi\fBo\fP outfile ] file1 ...
  464. X.SH DESCRIPTION
  465. XThe text formatting program \fInroff\fP(1) produces output
  466. Xthat is very unpleasant to print on dot matrix or daisy wheel printers.
  467. X\fINroff\fP produces an underlined word such as ``\o'f_'\o'o_'\o'o_'''
  468. Xby generating the output
  469. X``_^Hf_^Ho_^Ho'', which causes a print head to reverse direction six times.
  470. X(Here ``^H'' means ``backspace.'')
  471. XThis is slow, noisy, and probably causes excessive wear on the print head
  472. Xdrive.
  473. XWhen the \fBboldface\fP font is achieved by overstriking the situation is even
  474. Xworse.
  475. X\fIRoffix\fP is a post-filter for \fInroff\fP that fixes this problem by
  476. Xchanging ``_^Hf_^Ho_^Ho'' to
  477. X``foo^H^H^H___'', which causes only two reversals of the print head.
  478. X.PP
  479. XFiles listed on the command line are read as input, unless there are none,
  480. Xin which case standard input is used.
  481. XThe input files should have been generated by \fInroff\fP.
  482. XThe output goes to \fIoutfile\fP if the \(mi\fBo\fP option is used,
  483. Xotherwise to standard output.
  484. X.PP
  485. XThe \fIcount\fP given with the \(mi\fBs\fP option requires a bit of
  486. Xexplanation.
  487. XSuppose we want to output
  488. X.ti +4m
  489. X\o'f_'\o'o_'\o'o_', \o'b_'\o'a_'\o'r_'
  490. X.br
  491. XThis is best handled as a single unit, i.e.,
  492. X.ti +4m
  493. Xfoo, bar^H^H^H^H^H^H^H^H___  ___
  494. X.br
  495. XIf the spacing is greater, e.g.,
  496. X.ti +4m
  497. X\o'f_'\o'o_'\o'o_',                              \o'b_'\o'a_'\o'r_'
  498. X.br
  499. Xit's probably faster to backspace separately for each word, i.e.,
  500. X.ti +4m
  501. Xfoo^H^H^H___,                              bar^H^H^H___
  502. X.br
  503. X\fICount\fP is the maximum number of consecutive non-overstruck characters that
  504. Xcan be between two groups of overstruck characters and still have the
  505. Xbackspacing for both groups be done at once.
  506. XThe default for \fIcount\fP is 4.
  507. X.PP
  508. XThe backspacing for the characters that precede a control character 
  509. Xor escape sequence is always
  510. Xdone separately from the backspacing for the characters that follow.
  511. XThe control character or escape sequence itself is always output verbatim,
  512. Xand is not involved in backspace processing.
  513. XAn escape sequence is always assumed to consist of ESC followed by one
  514. Xcharacter.
  515. XThe characters SO (ASCII \\016) and SI (ASCII \\017) are assumed to begin
  516. Xand end a sequence of characters from an alternate character set.
  517. XThey and the characters they delimit are output verbatim without any
  518. Xprocessing of imbedded backspaces.
  519. X\fIRoffix\fP recognizes the following control characters and escape sequences
  520. Xfor the purpose of keeping track of the current line number:
  521. Xnew line, return, form feed, ESC 7 (reverse line feed), ESC 8 (reverse half
  522. Xline feed), ESC 9 (forward half line feed), and VT (ASCII \\013 \(mi
  523. Xinterpreted as reverse line feed for compatibility with old programs).
  524. X.PP
  525. XWarnings are issued if there is any backspacing over a tab, new line, return,
  526. Xor form feed.
  527. XBackspacing over other control characters is done without warnings;
  528. Xit is assumed \fInroff\fP knows what it's doing.
  529. X.PP
  530. XThe return value of \fIroffix\fP is the number of errors encountered (0 if
  531. Xno errors).
  532. X.SH EXAMPLE
  533. Xnroff \(miTlp \(miman roffix.1 | roffix | lp
  534. X.SH SEE ALSO
  535. X\fInroff\fP(1), \fIcol\fP(1)
  536. X.SH BUGS
  537. X\fIRoffix\fP reverses the order in which characters are overstruck.
  538. XThis should not matter on any normal hardcopy printer.
  539. XMulti-character escape sequences may get garbled.
  540. XBackspacing over
  541. Xcontrol sequences that make the printer space horizontally or
  542. Xvertically in strange ways will probably confuse \fIroffix\fP.
  543. END_OF_FILE
  544. if test 3560 -ne `wc -c <'roffix.1'`; then
  545.     echo shar: \"'roffix.1'\" unpacked with wrong size!
  546. fi
  547. # end of 'roffix.1'
  548. fi
  549. echo shar: End of archive 1 \(of 1\).
  550. cp /dev/null ark1isdone
  551. MISSING=""
  552. for I in 1 ; do
  553.     if test ! -f ark${I}isdone ; then
  554.     MISSING="${MISSING} ${I}"
  555.     fi
  556. done
  557. if test "${MISSING}" = "" ; then
  558.     echo You have the archive.
  559.     rm -f ark[1-9]isdone
  560. else
  561.     echo You still need to unpack the following archives:
  562.     echo "        " ${MISSING}
  563. fi
  564. ##  End of shell archive.
  565. exit 0
  566.  
  567.